package cn.bonoon.kernel.menus;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.util.Assert;
import org.w3c.dom.Element;

import cn.bonoon.kernel.VisibleLevel;
import cn.bonoon.kernel.VisibleScope;
import cn.bonoon.kernel.config.ConfigurableBeanDefinitionParser;
import cn.bonoon.kernel.menus.tips.MenuTipDefinition;
import cn.bonoon.kernel.menus.tips.MenuTipType;
import cn.bonoon.kernel.util.StringHelper;

/**
 * 
 * @author jackson
 * @version 2012-6-20 添加在样式中对系统名称的图片支持
 * 
 */
public final class ModulesDefinitionParser extends ConfigurableBeanDefinitionParser {

	protected Class<?> getBeanClass(Element element) {
		return resolveClass(element, StandardModuleManager.class);
	}

	protected void doParse(Element element, BeanDefinitionBuilder bean) {
		
		bean.addPropertyValue("id", resolveId(element));
		//这个路径是必填的，如：s，表示这个平台以"/s"开始可以访问
		bean.addPropertyValue("path", element.getAttribute("path"));
		bean.addPropertyValue("resourceServer", resolveText(element, "resource-server"));
		bean.addPropertyValue("documentFolder", resolveText(element, "document-folder"));
		
		String name = internalParseStyle(bean, element(element, "style"));
		bean.addPropertyValue("name", name);
		
		internalParseEmbed(bean, element);
		internalParseDescribe(bean, element);
		internalParseLogin(bean, element);
		
		ManagedList<BeanDefinition> voters =  new ManagedList<BeanDefinition>();
		for (Element eleModule : elements(element, "module")) {
			BeanDefinitionBuilder moduleBuilder = BeanDefinitionBuilder.rootBeanDefinition(ModuleDefinitionImpl.class);
			moduleBuilder.addPropertyValue("id", resolveId(eleModule));
			//这个是子系统的路径，可以不填，如果有填，则这个子系统下面的菜单、使用相对路径的，则会自动把"平台路径+本路径"来作为菜单的前缀
			moduleBuilder.addPropertyValue("path", eleModule.getAttribute("path"));
			moduleBuilder.addPropertyValue("ordinal", resolveOrdinal(eleModule));

			name = internalParseStyle(moduleBuilder, eleModule);
			moduleBuilder.addPropertyValue("name", name);
			
			internalParseEmbed(moduleBuilder, eleModule);
			internalParseDescribe(moduleBuilder, eleModule);
			
			List<ChannelDefinitionImpl> channels = new ArrayList<ChannelDefinitionImpl>();
			for (Element eleChannel : elements(eleModule, "channel")) {
				ChannelDefinitionImpl channelInfo = new ChannelDefinitionImpl();
				internalParseMenu(channelInfo, eleChannel);
				
				channels.add(channelInfo);
				//TODO 读取tabs信息
				internalParseTabs(channelInfo, eleChannel);
			}
			
			moduleBuilder.addConstructorArgValue(channels);
			voters.add(moduleBuilder.getBeanDefinition());
		}
		bean.addConstructorArgValue(voters);
	}

	private void internalParseMenu(AbstractMenuable item, Element element) {
		internalParseMenuAttributes(item, element);
		
		for (Element eleMenu : elements(element, "menu")) {
			
			MenuDefinitionImpl menu = new MenuDefinitionImpl();
			menu.setIconCls(eleMenu.getAttribute("iconCls"));
			menu.setAt(eleMenu.getAttribute("at"));
			
			internalParseMenu(menu, eleMenu);
			
			item.add(menu);
		}
	}

	private String internalParseStyle(BeanDefinitionBuilder bean, Element style) {
		
		String name = resolveName(style);
		Assert.hasText(name, "必须定义系统(子系统)的名称！");
		
		BeanDefinitionBuilder styleBuilder = BeanDefinitionBuilder.rootBeanDefinition(StyleDefinitionImpl.class);
		
		styleBuilder.addPropertyValue("topHeight", resolveInt(style, "top-height"));
		styleBuilder.addPropertyValue("menuWidth", resolveInt(style, "menu-width"));
		styleBuilder.addPropertyValue("footHeight", resolveInt(style, "foot-height"));
		
		styleBuilder.addPropertyValue("name", name);
		styleBuilder.addPropertyValue("thumbnail", resolveValue(style, "thumbnail"));
		styleBuilder.addPropertyValue("logo", resolveValue(style, "logo"));
		styleBuilder.addPropertyValue("foot", resolveText(style, "foot"));
		styleBuilder.addPropertyValue("resourceFolder", resolveText(style, "resource-folder"));
		
		bean.addPropertyValue("style", styleBuilder.getBeanDefinition());
		
		return name;
	}
	
	private void internalParseLogin(BeanDefinitionBuilder bean, Element element){
		Element eleLogin = element(element, "login");
		if(null != eleLogin){
			BeanDefinitionBuilder loginPageBuilder = BeanDefinitionBuilder.rootBeanDefinition(LoginPageDefinitionImpl.class);
			loginPageBuilder.addPropertyValue("name", resolveValue(eleLogin, "name"));
			loginPageBuilder.addPropertyValue("foot", resolveValue(eleLogin, "foot"));
			loginPageBuilder.addPropertyValue("logo", resolveValue(eleLogin, "logo"));

			loginPageBuilder.addPropertyValue("layout", eleLogin.getAttribute("layout"));
			
			bean.addPropertyValue("loginPage", loginPageBuilder.getBeanDefinition());
		}
	}
	
	private void internalParseEmbed(BeanDefinitionBuilder moduleBuilder, Element eleModule){
		List<PartDefinition> parts = new ArrayList<PartDefinition>();
		for(Element elePart : elements(eleModule, "embed")){
			String url = elePart.getAttribute("url");
			if(StringHelper.isEmpty(url)){ continue; }
			
			String name = resolveName(elePart);
			if(StringHelper.isEmpty(name)){ continue; }
			
			PartDefinition hpi = new PartDefinition(name, url);
			hpi.setOrdinal(resolveOrdinal(elePart));
			hpi.setLevel(VisibleLevel.valueOf(resolveValue(elePart, "level")));// getInt(elePart, "level"));
			hpi.setHelper(resolveBoolean(elePart, "helper"));
			hpi.setIconCls(elePart.getAttribute("iconCls"));
			parts.add(hpi);
		}
		moduleBuilder.addPropertyValue("parts", parts);
	}
	
	private void internalParseDescribe(BeanDefinitionBuilder moduleBuilder, Element eleModule){
		DescribeDefinitionImpl des = internalParseDescribe(eleModule);
		if(null != des){
			moduleBuilder.addPropertyValue("describe", des);
		}
	}
	
	private DescribeDefinitionImpl internalParseDescribe(/*BeanDefinitionBuilder moduleBuilder, */Element element){
		/*
		 * TODO 这个是需要重新修改的
		 */
		Element eleDes = element(element, "des");
		if(null != eleDes){
			DescribeDefinitionImpl des = new DescribeDefinitionImpl();
			des.setShowDocument(resolveBoolean(eleDes, "show", true));
			des.setFolder(eleDes.getAttribute("folder"));
			for(Element eleDoc : elements(eleDes, "document")){
				des.add(eleDoc.getAttribute("name"), eleDoc.getAttribute("path"));
			}
			return des;
		}
		
		return null;
	}

	private void internalParseTabs(ChannelDefinitionImpl channel, Element element) {
		for (Element eleTabs : elements(element, "tabs")) {
			TabsDefinition tabs = new TabsDefinition(channel);
			tabs.setId(resolveId(eleTabs));
			tabs.setName(resolveName(eleTabs));
			tabs.setOrdinal(resolveOrdinal(eleTabs));
			tabs.setTitle(resolveValue(eleTabs, "title"));
			tabs.setDescribe(internalParseDescribe(eleTabs));
			
			for (Element eleTab : elements(eleTabs, "tab")) {
				TabItemParser tabParser = null;
				Class<?> parserClass = resolveClass(eleTab, null);
				if(null == parserClass){
					String href = resolveValue(eleTab, "href");
					if(StringHelper.isEmpty(href)){
						continue;
					}
					tabParser = new TabItemSimpleParser(href, eleTab.getAttribute("type"));
				}else{
					tabParser = new TabItemClassParser(parserClass, eleTab.getAttribute("scope"));
				}
				
				TabItemDefinition tab = new TabItemDefinition(tabs, tabParser);
				tab.setId(resolveId(eleTab));
				tab.setName(resolveName(eleTab));
				tab.setOrdinal(resolveOrdinal(eleTab));
				tabs.add(tab);
			}
			channel.add(tabs);
		}
	}
	
	private VisibleScope internalParseScope(Element element){
		String scope = element.getAttribute("scope");
		if(StringHelper.isNotEmpty(scope)){
			try{
				return VisibleScope.valueOf(scope);
			}catch(Exception ex){}
		}
		return VisibleScope.DOMAIN;
	}

	private void internalParseMenuAttributes(AbstractMenuable item, Element element) {
		item.setId(resolveId(element));
		item.setName(resolveName(element));
		item.setOrdinal(resolveOrdinal(element));
		
		item.setTitle(resolveValue(element, "title"));
		item.setAttrs(resolveValue(element, "attrs"));
		item.setArgs(resolveValue(element, "args"));
		item.setTarget(resolveValue(element, "target"));
		item.setHref(resolveValue(element, "href"));
		
		VisibleScope scope = internalParseScope(element);
		item.setScope(scope);

		item.setMenuTip(internalParseTip(element, scope));
		item.setDescribe(internalParseDescribe(element));

		List<ActionDefinition> actions = new ArrayList<ActionDefinition>();
		int actionPrivileges = 0;
		int i = 0;
		for (Element eleAction : elements(element, "action")) {
			//TODO 这里需要被修改
//			int key = getInt(eleAction, "key");
			actions.add(new ActionDefinition(item, i, eleAction.getAttribute("key"), resolveName(eleAction)));
			actionPrivileges = actionPrivileges | (1 << i ++);
		}
		
		int size = actions.size();
		if(size > 0){
			item.setActions(actions.toArray(new ActionDefinition[size]));
		}
		item.setActionPrivileges(actionPrivileges);
	}
	
	private MenuTipDefinition internalParseTip(Element element, VisibleScope scope){

		Element eleTip = element(element, "tip");
		if(null == eleTip) return null;
		
		MenuTipDefinition tip = new MenuTipDefinition();
		tip.setScope(scope);
		tip.setValue(resolveValue(eleTip, "value"));
		tip.setFormat(resolveValue(eleTip, "format"));
		
		tip.setType(resolveEnum(eleTip, "type", MenuTipType.class));
		tip.setRefreshable(resolveBoolean(eleTip, "refreshable"));
		tip.setInHome(resolveBoolean(eleTip, "inHome"));
		
		return tip;
	}
}
